package stocks.server;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;

public class StockQueue {
	HashMap<String,Stock> stocks;
	HashMap<String,PriorityQueue<QueuedStock>> pendingSales;
	HashMap<String,PriorityQueue<QueuedStock>> pendingPurchases;
	int trans;
	Server server;
	
	StockQueue(Server server) {
		trans = 0;
		this.server = server;
		stocks = new HashMap<String,Stock>();
		pendingSales = new HashMap<String,PriorityQueue<QueuedStock>>();
		pendingPurchases = new HashMap<String,PriorityQueue<QueuedStock>>();
	}
	
	/**
	 * Add a stock to be sold.
	 * @param stock 
	 * @param ownerCallback
	 */
	public void addStock(Stock stock,CallBack ownerCallback) {
		stocks.put(stock.getName(), stock);
		// setup some intial stuff
		PriorityQueue<QueuedStock> newQueue = new PriorityQueue<QueuedStock>();
		newQueue.add(new QueuedStock(stock.getName(), stock.getPrice(), stock.getOwner(), mode.SELL, stock.getQuantity(),ownerCallback));
		pendingSales.put(stock.getName(),newQueue);
	}
	
	/**
	 * Push a new stock to be sold, this will callback the client once its sold
	 * @param name Stock name
	 * @param price Price
	 * @param owner Owner of the stock
	 * @param qunatity Quantity to sell.
	 * @param ownerCallback Callback
	 * @return
	 */
	public boolean pushSelling(String name,int price, String owner,int qunatity,CallBack ownerCallback) {
		Stock theStock = stocks.get(name);
		int oldPrice = theStock.getPrice();
		
		int p10 = oldPrice - 10;
		int m10 = oldPrice + 10;
		if(!(price >= p10 && price <= m10)) {
			return false;
		}
		
		QueuedStock newStock = new QueuedStock(name,price,owner,mode.SELL,qunatity,ownerCallback);
		PriorityQueue<QueuedStock> targetList;
		targetList = pendingSales.get(name);
		targetList.add(newStock);
		return true;
	}
	
	/**
	 * Push a new stock to be bought, this will callback the client once its bought
	 * @param name Stock name
	 * @param price Price
	 * @param owner Owner of the stock
	 * @param qunatity Quantity to sell.
	 * @param ownerCallback Callback
	 * @return
	 */
	public boolean pushBuying(String name,int price, String owner,int quantity,CallBack ownerCallback) {
		Stock theStock = stocks.get(name);
		int oldPrice = theStock.getPrice();
		
		int p10 = oldPrice - 10;
		int m10 = oldPrice + 10;
		if(!(price >= p10 && price <= m10)) {
			return false;
		}
		
		QueuedStock newStock = new QueuedStock(name,price,owner,mode.BUY,quantity,ownerCallback);
		PriorityQueue<QueuedStock> targetList;
		targetList = pendingSales.get(name);
		targetList.add(newStock);
		return true;
	}
	
	/**
	 * Should be used to remove stocks from the sell queue. and process all buying and selling
	 * @param name
	 * @param owner
	 * @param priceWanted
	 * @param quantity
	 * @return
	 */
	
	public void doBuyStocks() {
		Iterator<String> outer;
		outer = pendingPurchases.keySet().iterator();
		while(outer.hasNext()) {
			String name = outer.next();
			PriorityQueue<QueuedStock> queue = pendingPurchases.get(name);
			QueuedStock currentStock; 
			Iterator<QueuedStock> it = queue.iterator();
			while(it.hasNext()) {
				QueuedStock wantsToBuy = it.next();
				if(buyStocks(name,wantsToBuy.getName(),wantsToBuy.getPrice(),wantsToBuy.getQuantity(),wantsToBuy.getOwnerCallback()) > 0) {
					// we sold some shit!
					queue.remove(wantsToBuy);
					
				}
			}
		}
	}
	
	/**
	 * Generate unique ID
	 * @return
	 */
	public String genCode() {
		Date date = new Date();
		DateFormat df = new SimpleDateFormat("dd-MM-yyyy");  
		   
		return String.format("%s%08d",df.format(date.getTime()),trans++);  
	}
	
	/**
	 * Buy a particular stock
	 * @param name Stock name
	 * @param owner Owning user
	 * @param priceWanted Maximum price
	 * @param quantity Quantity to buy
	 * @param ownerCallback callback
	 * @return
	 */
	public int buyStocks(String name,String owner, int priceWanted,int quantity,CallBack ownerCallback) {
		Stock theStock = stocks.get(name); // for trade price
		PriorityQueue<QueuedStock> queue = pendingSales.get(name);
		QueuedStock currentStock; 
		Iterator<QueuedStock> it = queue.iterator();
		CallBack sellerCallBack;
		Stack<QueuedStock> salesStack = new Stack<QueuedStock>();
		QueuedStock partial; // for leftovers
	
		int bought = 0;
		int paid = 0;
		int last_trade = 0;
		int justbought = 0;
		int justpaid = 0;
		
		while (it.hasNext()) {
		   if(bought == quantity) { // we've got all we need
			   break;
		   }
		   
		   currentStock = it.next();
		   if(currentStock.getPrice() <= priceWanted) {
			   if(currentStock.getQuantity() > (quantity - bought)) {
				   // definte last sale
				   partial = currentStock;
				   currentStock.setQuantity(currentStock.getQuantity() - (quantity - bought));
				   justpaid = currentStock.getQuantity() * (quantity - bought);
				   paid += currentStock.getQuantity() * (quantity - bought);
				   justbought = (quantity - bought);
				   bought += (quantity - bought);
				   last_trade = currentStock.getPrice();
				   try {
					   sellerCallBack = currentStock.getOwnerCallback();
					   // public void addCompletedOrder(String transactionID,String stockName,String userName,int qty,int price)
					   server.addCompletedOrder(genCode(), name, ownerCallback.getName(), justbought, justpaid);
					   sellerCallBack.transactionCompleteSold("Sold " + bought + " " + currentStock.getName() + " " + " for " + paid);
				   } catch (Exception e) {
					   e.printStackTrace();
					   System.exit(0);
				   }
				   break;
			   } else {
				   // not all of it, eat it and keep on going, if its equal it will break after.
				   bought += currentStock.getQuantity();
				   justbought = currentStock.getQuantity();
				   paid += currentStock.getQuantity() * currentStock.getPrice();
				   justpaid = currentStock.getQuantity() * currentStock.getPrice();
				   last_trade = currentStock.getPrice();
				   try {
					   sellerCallBack = currentStock.getOwnerCallback();
					   server.addCompletedOrder(genCode(), name, ownerCallback.getName(), justbought, justpaid);
					   sellerCallBack.transactionCompleteSold("Sold " + bought + " " + currentStock.getName() + " " + " for " + paid);
				   } catch (Exception e) {
					   e.printStackTrace();
					   System.exit(0);
				   }
				   queue.remove(currentStock);
			   }
		   }
		}
		
		if(paid > 0) {
			theStock.setPrice(last_trade);
		}
		return paid;
	}
}
